﻿using Common.Infrastructure.Structs;
using Common.Infrastructure.Utils;
using Consul;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Common.Infrastructure.Service
{
    public class CommonCacheService : IComonCacheService
    {
        private static CommonCache commonCache = null;
        private static ConsulClient consulClient = null;
        private static string apiUrl = string.Empty;
        private static SortedDictionary<string, OrgCache> orgByIds = null;
        private static SortedDictionary<string, OrgCache> orgByCodes = null;
        private static SortedDictionary<string, OrgCacheTreeNode> orgTreeNodes = null;
        private readonly IConfiguration configuration;

        public CommonCacheService(IConfiguration serviceCallService)
        {
            this.configuration = serviceCallService; 
        }

        public OrgCache GetOrgById(string orgId)
        {
            if (string.IsNullOrEmpty(orgId))
            {
                return null;
            }

            CheckAndGetOrg();
            if (orgByIds == null)
            {
                orgByIds = new SortedDictionary<string, OrgCache>();
            }

            return orgByIds.TryGetValue(orgId, out OrgCache ret) ? ret : null;
        }

        public OrgCache GetOrgByCode(string orgCode)
        {
            if (string.IsNullOrEmpty(orgCode))
            {
                return null;
            }

            CheckAndGetOrg();
            if (orgByCodes == null)
            {
                orgByCodes = new SortedDictionary<string, OrgCache>();
            }

            return orgByCodes.TryGetValue(orgCode, out OrgCache ret) ? ret : null;
        }

        public OrgCacheTreeNode GetOrgTreeNodeById(string orgId)
        {
            if (string.IsNullOrEmpty(orgId))
            {
                return null;
            }

            CheckAndGetOrg();
            if (orgTreeNodes == null)
            {
                orgTreeNodes = new SortedDictionary<string, OrgCacheTreeNode>();
            }

            return orgTreeNodes.TryGetValue(orgId, out OrgCacheTreeNode ret) ? ret : null;
        }

        public string GetGroupUrl()
        {
            CheckAndGetOrg();
            return commonCache?.GroupUrl;
        }

        public string GetApiKey()
        {
            return this.configuration["ApiKey"];
        }

        public string GetServiceCode()
        {
            return this.configuration["ServiceCode"];
        }

        public void GetCommonCache()
        {
            this.CheckAndGetOrg();
        }

        private string GetUrl(string moduleCode)
        { 
            if (!string.IsNullOrEmpty(apiUrl))
            {
                return apiUrl;
            }

            apiUrl = configuration["CacheUrl"];
            if (!string.IsNullOrEmpty(apiUrl))
            {
                return apiUrl;
            }

            moduleCode = (string.IsNullOrEmpty(GetApiKey()) ? string.Empty : (GetApiKey() + "_")) + moduleCode;
            var url = this.configuration["Consul:Address"];
#if DEBUG
            url = "http://localhost:8500/";
#endif
            var http = this.configuration["Consul:Http"];
            if (consulClient == null)
            {
                consulClient = new ConsulClient(options =>
                {
                    options.Address = new Uri(url);
                });
            }

            var result = consulClient.Health.Service(
                moduleCode,
                null,
                true,
                new QueryOptions
                {
                    WaitTime = TimeSpan.FromSeconds(1)
                }).Result;
            var httpModel = !string.IsNullOrEmpty(http) ? http : "http";
            var consultResponse = result.Response.FirstOrDefault();
            if (consultResponse == null)
            {
                throw new ArgumentNullException("consultResponse");
            }

            apiUrl = $"{httpModel}://{consultResponse.Service.Address}:{consultResponse.Service.Port}";
            return apiUrl;
        }

        private void CheckAndGetOrg()
        {
            try
            {
                if (commonCache == null)
                {
                    var servceCode = this.configuration["CacheServiceCode"];
                    if (string.IsNullOrEmpty(servceCode))
                    {
                        servceCode = "Base";
                    }

                    var host = GetUrl(servceCode);
                    var url = host + "/api/BaseData/GetCommonCache";
                    WebApiPostRequest request = new WebApiPostRequest
                    {
                        Url = url,
                    };
                    var response = WebApiUtils.PostAsync(request).Result;
                    if (!string.IsNullOrEmpty(response.Data))
                    {
                        var libOrgs = response.Data.ToObjectFromJson<ApiActionResult<CommonCache>>();
                        if (libOrgs != null && libOrgs.Data != null)
                        {
                            commonCache = libOrgs.Data;
                            if (commonCache.OrgCaches != null && commonCache.OrgCaches.Any())
                            {
                                orgByIds = GetOrgByList(commonCache.OrgCaches);
                                orgByCodes = GetOrgByCodeByList(commonCache.OrgCaches);
                                orgTreeNodes = GetOrgTreeByList(commonCache.OrgCaches);
                            }
                        }
                    }
                }
            }
            catch
            {
            }
        }

        private SortedDictionary<string, OrgCache> GetOrgByList(List<OrgCache> orgs)
        {
            SortedDictionary<string, OrgCache> ret = new SortedDictionary<string, OrgCache>();
            foreach (var item in orgs)
            {
                ret[item.Id] = item;
            }

            return ret;
        }

        private SortedDictionary<string, OrgCache> GetOrgByCodeByList(List<OrgCache> orgs)
        {
            SortedDictionary<string, OrgCache> ret = new SortedDictionary<string, OrgCache>();
            foreach (var item in orgs)
            {
                ret[item.Code] = item;
            }

            return ret;
        }

        private SortedDictionary<string, OrgCacheTreeNode> GetOrgTreeByList(List<OrgCache> orgs)
        {
            SortedDictionary<string, OrgCacheTreeNode> ret = new SortedDictionary<string, OrgCacheTreeNode>();
            DoGetOrgTreeByOrg(orgs, null, null, ret);
            return ret;
        }

        private void DoGetOrgTreeByOrg(
            List<OrgCache> orgs,
            string parentId,
            OrgCacheTreeNode parentTreeNode,
            SortedDictionary<string, OrgCacheTreeNode> ret)
        {
            var tempOrgs = orgs.Where(t => t.ParentId == parentId).ToList();
            if (!tempOrgs.Any())
            {
                return;
            }

            foreach (var item in tempOrgs)
            {
                OrgCacheTreeNode orgCache = new OrgCacheTreeNode();
                orgCache.OrgId = item.Id;
                orgCache.Org = item;
                ret[item.Id] = orgCache;
                if (parentTreeNode != null)
                {
                    parentTreeNode.AllChildrenIds.Add(item.Id);
                    if (parentTreeNode.AllParentIds != null && parentTreeNode.AllParentIds.Any())
                    {
                        orgCache.AllParentIds.AddRange(parentTreeNode.AllParentIds);
                        foreach (var pId in parentTreeNode.AllParentIds)
                        {
                            OrgCacheTreeNode parentC = null;
                            if (ret.TryGetValue(pId, out parentC))
                            {
                                if (!parentC.AllChildrenIds.Contains(item.Id))
                                {
                                    parentC.AllChildrenIds.Add(item.Id);
                                }
                            }
                        }
                    }

                    orgCache.AllParentIds.Add(parentTreeNode.OrgId);
                }

                this.DoGetOrgTreeByOrg(orgs, item.Id, orgCache, ret);
            }
        }
    }
}
